#include "stdafx.h"
#include "Bound.h"
#include "Math.h"
#include "Matrix3.h"

Bound::Bound()
	:m_center( Vector3::ZERO )
{
	m_radius = 0.0;
}

Bound& Bound::operator= ( const Bound& bound )
{
	m_center = bound.m_center;
	m_radius = bound.m_radius;
	return *this;
}

Bound& Bound::operator+= ( const Bound& bound )
{
	static const Real tol = 1e-06;

	// difference of current sphere center and input sphere center
	Vector3 diff = m_center - bound.m_center;

	Real len_sqr = diff.squared_length();
	Real delta   = bound.m_radius - m_radius;
	Real delta_sqr = delta * delta;
	Real length, alpha;

	if ( delta >= 0.0 )
	{
		if ( delta_sqr >= len_sqr )
		{
			// input sphere encloses this sphere
			m_center = bound.m_center;
			m_radius = bound.m_radius;
		}
		else
		{
			// this sphere does not enclose input sphere
			length = Math::sqrt_t( len_sqr );
			if ( length > tol )
			{
				alpha = ( length - delta ) / ( 2.0*length );
				m_center = bound.m_center - alpha * diff;
			}
			m_radius = 0.5 * ( bound.m_radius + length + m_radius );
		}
	}
	else if ( delta_sqr < len_sqr )
	{
		// this sphere does not enclose input sphere
		length = Math::sqrt_t( len_sqr );
		if ( length > tol )
		{
			alpha = ( length - delta ) / (2.0*length);
			m_center = bound.m_center + alpha*diff;
		}
		m_radius = 0.5*( bound.m_radius + length + m_radius );
	}
	// else this sphere enclose input sphere

	return *this;
}

void Bound::compute_from_data( unsigned int vertices, const Vector3* data )
{
	if ( vertices == 0 )
	{
		m_center = Vector3::ZERO;
		m_radius = 0.0;
		return;
	}

	// compute the axis-aligned box containing the data
	Vector3 min = data[0], max = min;
	for ( unsigned int i = 1; i < vertices; i++ )
	{
		if ( min[0] > data[i][0] )
			min[0] = data[i][0];
		if ( min[1] > data[i][1] )
            min[1] = data[i][1];
        if ( min[2] > data[i][2] )
            min[2] = data[i][2];
        if ( max[0] < data[i][0] )
            max[0] = data[i][0];
        if ( max[1] < data[i][1] )
            max[1] = data[i][1];
        if ( max[2] < data[i][2] )
            max[2] = data[i][2];
    }
	// sphere center is the axis-aligned box center
	m_center = 0.5 * ( max + min );

	// compute the radius
	Real radius_sqr = 0.0;
	for ( unsigned int i = 0; i < vertices; i++ )
	{
		Vector3 diff = data[i] - m_center;
		Real len_sqr = diff.squared_length();
		if ( len_sqr > radius_sqr )
			radius_sqr = len_sqr;
	}
	m_radius = Math::sqrt_t( radius_sqr );
}

	
Plane::Side Bound::which_side( const Plane& plane ) const
{
	Real ps = plane.distance_to( m_center );

	if ( ps <= -m_radius )
	{
		return Plane::NEGATIVE_SIDE;
	}
	else if ( ps >= m_radius )
	{
		return Plane::POSITIVE_SIDE;
	}
	else
	{
		return Plane::NO_SIDE;
	}
}
